package com.example.a10.controller;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpRequest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.a10.common.ErrorCode;
import com.example.a10.exception.BusinessException;
import com.example.a10.model.entity.Result;
import com.example.a10.mapper.CosManager;
import com.example.a10.model.vo.modelFileVo;
import com.example.a10.model.vo.trainFileVo;
import com.example.a10.newentity.*;
import com.example.a10.service.*;
import com.example.a10.util.*;
import com.example.a10.model.entity.Document;
import com.example.a10.model.entity.PredictDocument;
import com.example.a10.python.PythonProcedure;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.qcloud.cos.model.COSObjectInputStream;
import io.swagger.annotations.ApiOperation;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayOutputStream;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

@RestController
@RequestMapping("/Model")
public class ModelController {

    @Autowired
    PythonProcedure pythonProcedure;
    @Autowired
    HttpServletResponse response;
    @Autowired
    JsonServer jsonServer;
    @Autowired
    modelFileService modelFileservice;
    @Autowired
    CosManager cosManager;
    @Autowired
    featureFileService featureFileService;
    @Autowired
    resultFileService resultFileservice;
    @Autowired
    testFileService testFileService;

    @ApiOperation("接受训练数据文件名称,训练模型")
    @PostMapping( "/Train")
    @Transactional(rollbackFor = Exception.class) // 添加事务注解
    public Result TrainModel(@RequestBody trainFile trainFile) throws JsonProcessingException {
        // 记录程序开始时间
        long startTime = System.currentTimeMillis();
        String TrainName = trainFile.getFileName();
        String userId = trainFile.getUserId();
        if (TrainName!=null){
            //1.数据存进云服务器
            String port = PythonPort.TrainPort;
            String url = trainFile.getUrl();
            HttpRequest request = HttpRequest.post(port)
                    .form("fileName", TrainName)
                    .form("data_path", url);
            String response = request.execute().body();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(response);
            //2.存进数据库
            String FileUrl = jsonNode.get("data").get("FilePath").asText();
            long ModelSize = Long.parseLong(jsonNode.get("data").get("ModelSize").asText());
            modelFile modelFile = new modelFile();
            String uuid = UUIDUtil.getUUID();
            String trainFileId = trainFile.getId();
            int lastIndex = FileUrl.lastIndexOf('/');
            String fileName = FileUrl.substring(lastIndex + 1);
            String FileSize = DocumentUtil.getFileSize(ModelSize);
            modelFile.setFileName(fileName);
            modelFile.setTrainId(trainFileId);
            modelFile.setId(uuid);
            modelFile.setUserId(userId);
            modelFile.setUrl(FileUrl);
            modelFile.setCreateTime(LocalDateTime.now());
            modelFile.setFileSize(FileSize);
            modelFileservice.save(modelFile);
            // 记录程序结束时间
            long endTime = System.currentTimeMillis();
            // 计算程序运行时间（以毫秒为单位）
            long totalTimeMillis = endTime - startTime;
            // 转换为秒钟
            long totalSeconds = totalTimeMillis / 1000;
            long minutes = totalSeconds / 60;
            long seconds = totalSeconds % 60;
            String runtime = "";
            if (minutes > 0) {
                runtime += minutes + "分钟";
            }
            runtime += seconds + "秒";

            trainFileVo trainFileVo = new trainFileVo();
            trainFileVo.setFileName(TrainName);
            trainFileVo.setRuntime(runtime);
            return Result.success(trainFileVo);
        }
        return Result.fail("failed");
    }

    @ApiOperation("接受测试数据文件名称和模型文件的名称，预测数据")
    @PostMapping("/Predit")
    @Transactional(rollbackFor = Exception.class) // 添加事务注解
    public String PreditModel(@RequestBody PredictDocument predictDocument) throws JsonProcessingException {
        if(predictDocument!=null){
            String modelName = predictDocument.getModelName();
            String modelId = predictDocument.getModelId();
            String modelurl = predictDocument.getModelPath();
            String testName = predictDocument.getTestName();
            String testurl = predictDocument.getTestPath();
            String testId = predictDocument.getTestId();
            String userId = predictDocument.getUserId();

            //1.将数据存进云服务器
            String featurePort = PythonPort.PredictPort;
            HttpRequest request = HttpRequest.post(featurePort)
                    .form("modelFileName", modelName)
                    .form("testFileName", testName)
                    .form("modelDataPath", modelurl)
                    .form("testDataPath", testurl);
            String response = request.execute().body();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(response);
            //2.存进数据库
            resultFile resultFile = new resultFile();
            String resultUrl = jsonNode.get("data").get("FilePath").asText();
            long TestlSize = Long.parseLong(jsonNode.get("data").get("TestSize").asText());
            if(resultUrl!=null) {
                String FileSize = DocumentUtil.getFileSize(TestlSize);
                int lastIndex = resultUrl.lastIndexOf('/');
                String fileName = resultUrl.substring(lastIndex + 1);
                String uuid = UUIDUtil.getUUID();
                resultFile.setFileSize(FileSize);
                resultFile.setFileName(fileName);
                resultFile.setUrl(resultUrl);
                resultFile.setTestId(testId);
                resultFile.setModelId(modelId);
                resultFile.setCreateTime(LocalDateTime.now());
                resultFile.setId(uuid);
                resultFile.setUserId(userId);
                resultFileservice.save(resultFile);
                return "success";
            }else {
                return "failed";
            }
        }else {
            return "failed";
        }
    }

    @ApiOperation("预测多个数据")
    @PostMapping("/PreditMulti")
    @Transactional(rollbackFor = Exception.class) // 添加事务注解
    public String PreditMultiModel(@RequestBody Map<String, Object> requestBody) throws JsonProcessingException {
        String modelid = (String)requestBody.get("modelId");
        String userid = (String)requestBody.get("userId");
        String[] testids = ((List<String>)requestBody.get("testIds")).toArray(new String[0]);
        modelFile modelFile = modelFileservice.getById(modelid);
        String modelFileUrl = modelFile.getUrl();
        String modelFileFileName = modelFile.getFileName();
        for (int i = 0; i < testids.length; i++) {
            String testId=testids[i];
            testFile testFile = testFileService.getById(testId);
            String testFileUrl = testFile.getUrl();
            String testFileName = testFile.getFileName();
            //1.将数据存进云服务器
            String featurePort = PythonPort.PredictPort;
            HttpRequest request = HttpRequest.post(featurePort)
                    .form("modelFileName", modelFileFileName)
                    .form("testFileName", testFileName)
                    .form("modelDataPath", modelFileUrl)
                    .form("testDataPath", testFileUrl);
            String response = request.execute().body();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(response);
            //2.存到数据库
            resultFile resultFile = new resultFile();
            String resultUrl = jsonNode.get("data").get("FilePath").asText();
            long TestlSize = Long.parseLong(jsonNode.get("data").get("TestSize").asText());
            String FileSize = DocumentUtil.getFileSize(TestlSize);
            int lastIndex = resultUrl.lastIndexOf('/');
            String fileName = resultUrl.substring(lastIndex + 1);
            String uuid = UUIDUtil.getUUID();
            resultFile.setFileSize(FileSize);
            resultFile.setFileName(fileName);
            resultFile.setUrl(resultUrl);
            resultFile.setTestId(testId);
            resultFile.setModelId(modelid);
            resultFile.setCreateTime(LocalDateTime.now());
            resultFile.setId(uuid);
            resultFile.setUserId(userid);
            resultFileservice.save(resultFile);
        }
        return "success";
    }

    @ApiOperation("接受模型文件的名称,下载模型")
    @PostMapping("/download")
    public void Download(@RequestBody trainFile trainFile) throws IOException {
        String url = trainFile.getUrl();
        COSObjectInputStream cosObjectInputStream = cosManager.downloadFile(url);
        // 设置响应头信息
        response.setContentType("application/octet-stream"); // 设置响应类型为二进制流
        // 使用 ServletUtil.write() 方法将文件内容写入 ServletOutputStream
        ServletUtil.write(response, cosObjectInputStream);
    }

    @ApiOperation("返回所有模型文件的名称")
    @GetMapping("/list")
    public List<modelFile> list(String userid){
        LambdaQueryWrapper<modelFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(modelFile::getUserId,userid);
        List<modelFile> modelFileList = modelFileservice.list(lambdaQueryWrapper);
        return modelFileList;
    }

    @ApiOperation("返回模型文件名称，删除模型")
    @DeleteMapping("/delete")
    @Transactional(rollbackFor = Exception.class) // 添加事务注解
    public String deleteModel(@RequestBody modelFile modelFile){
        if(modelFile!=null){
            //删除数据库的数据
            String modelFileId = modelFile.getId();
            modelFileservice.removeById(modelFileId);
            QueryWrapper<featureFile> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("model_id", modelFileId);
            List<featureFile> list = featureFileService.list(queryWrapper);
            featureFileService.remove(queryWrapper);
            //2.删除云数据库的数据
            for(featureFile featurefile:list){
                String lgbUrl = featurefile.getLgbUrl();
                String shapUrl = featurefile.getShapUrl();
                try {
                    cosManager.deleteFile(lgbUrl);
                    cosManager.deleteFile(shapUrl);
                } catch (IOException e) {
                    return "success";
                }
            }
            String url = modelFile.getUrl();
            try {
                cosManager.deleteFile(url);
            } catch (IOException e) {
                return "success";
            }
            return "success";
        }else {
            return "failed";
        }
    }

    @PostMapping("/FeatureImport")
    public List<Map.Entry<String, Integer>> FeatureData(@RequestBody Document document) throws IOException {
        String modelname=document.getName();
        String featureName = modelname.replace(".txt", ".json");
        File file = new File(AddressConstants.Feature + featureName); // 创建文件对象
        boolean exists = file.exists();
        //如果不存在，则调用python生成
        if(exists==false){
           pythonProcedure.Feature(modelname,"test_2000_x.csv");
        } else{
            Map<String, Object> stringObjectMap = jsonServer.readfeatureJsonFile(featureName);
            Map<String, Integer> data = new HashMap<>();
            for (Map.Entry<String, Object> entry : stringObjectMap.entrySet()) {  // 遍历 Map，将数据类型转换为 Integer
                data.put(entry.getKey(), Integer.valueOf(entry.getValue().toString()));
            }
            // 对Map数据进行排序
            List<Map.Entry<String, Integer>> list = new ArrayList<>(data.entrySet());
            Collections.sort(list, Map.Entry.comparingByValue(Comparator.reverseOrder()));

            // 取出前十个最高的数据
            List<Map.Entry<String, Integer>> topTen = list.subList(0, Math.min(10, list.size()));
            return topTen;
        }
        return null;
    }

    @PostMapping("/GetFeatureImport")
    @Transactional(rollbackFor = Exception.class) // 添加事务注解
    public String GetFeatureData(@RequestBody PredictDocument predictDocument) throws IOException {
        String modelPath = predictDocument.getModelPath();
        String testPath = predictDocument.getTestPath();
        int lastIndex = modelPath.lastIndexOf('/');
        String modelName = modelPath.substring(lastIndex + 1);
        int testIndex = testPath.lastIndexOf('/');
        String testlName = testPath.substring(testIndex + 1);

        //1.数据存进云服务器
        String port = PythonPort.FeaturePort;
        HttpRequest request = HttpRequest.post(port)
                .form("modelFileName", modelName)
                .form("modelDataPath", modelPath)
                .form("testFileName", testlName)
                .form("testDataPath", testPath);
        String response = request.execute().body();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(response);
        //2.存进数据库
        String lgburl = jsonNode.get("data").get("lgb_feature_importance_result").asText();
        String shapurl = jsonNode.get("data").get("shap_feature_importance_result").asText();
        String uuid = UUIDUtil.getUUID();
        String modelId = predictDocument.getModelId();
        String testId = predictDocument.getTestId();

        featureFile featureFile = new featureFile();
        featureFile.setLgbUrl(lgburl);
        featureFile.setShapUrl(shapurl);
        featureFile.setCreateTime(LocalDateTime.now());
        featureFile.setId(uuid);
        featureFile.setModelId(modelId);
        featureFile.setTestId(testId);

        featureFileService.save(featureFile);
        return "sucess";
    }

    @PostMapping("/ResultFeatureData")
    @ResponseBody
    public Result ResultFeatureData(@RequestBody modelFileVo modelFile){
        String modelFileId = modelFile.getId();
        int number = Integer.parseInt(modelFile.getNumber());
        QueryWrapper<featureFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("model_id", modelFileId);
        List<featureFile> featureFiles = featureFileService.list(queryWrapper);
        String lgburl;
        String shapurl;
        COSObjectInputStream lgbdata;
        try {
            featureFile featureFile = featureFiles.get(0);
            lgburl = featureFile.getLgbUrl();
            shapurl = featureFile.getShapUrl();
            lgbdata = cosManager.downloadFile(lgburl);
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "还没有分析模型特征重要性,请先分析");
        }
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while (true) {
            try {
                if (!((bytesRead = lgbdata.read(buffer)) != -1)) break;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            outputStream.write(buffer, 0, bytesRead);
        }
        byte[] byteArray = outputStream.toByteArray();
        // 使用 Jackson 库将字节数组解析为 JSON 字符串
        String jsonString = new String(byteArray);
        // 将 JSON 字符串解析为 Map<String, Object> 对象
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> stringObjectMap = null;
        try {
            stringObjectMap = objectMapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {});
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        Map<String, Integer> data = new HashMap<>();
        for (Map.Entry<String, Object> entry : stringObjectMap.entrySet()) {  // 遍历 Map，将数据类型转换为 Integer
            data.put(entry.getKey(), Integer.valueOf(entry.getValue().toString()));
        }
        // 对Map数据进行排序
        List<Map.Entry<String, Integer>> list = new ArrayList<>(data.entrySet());
        Collections.sort(list, Map.Entry.comparingByValue(Comparator.reverseOrder()));
        // 取出前20个最高的数据
        List<Map.Entry<String, Integer>> topTen = list.subList(0, Math.min(number, list.size()));
        String[] featuresArray = new String[topTen.size()];
        for (int i = 0; i < topTen.size(); i++) {
            String feature = topTen.get(i).getKey();
            featuresArray[i] = feature;
        }
        COSObjectInputStream shapdata = null;
        try {
            shapdata = cosManager.downloadFile(shapurl);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 读取 JSON 文件内容
        // 创建一个字节数组输出流
        ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();
        byte[] buffer1 = new byte[1024];
        int bytesRead1;
        while (true) {
            try {
                if (!((bytesRead1 = shapdata.read(buffer1)) != -1)) break;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            // 将读取的数据写入字节数组输出流
            outputStream1.write(buffer1, 0, bytesRead1);
        }
        // 将字节数组输出流中的数据转换为字符串
        String jsonStr = new String(outputStream1.toByteArray(), StandardCharsets.UTF_8);
        // 将 JSON 字符串转换为 JSON 对象
        JSONObject jsonObject = new JSONObject(jsonStr);
        Map<String, Object> resultMap = new HashMap<>();
        for (String feature : featuresArray) {
            if (jsonObject.has(feature)) {
                resultMap.put(feature, jsonObject.get(feature));
            }
        }
        Gson gson = new Gson();
        String json = gson.toJson(resultMap);
        return Result.success(topTen,json);
    }

    @GetMapping("/findById")
    public modelFile findById(String id){
        modelFile modelFile = modelFileservice.getById(id);
        return modelFile;
    }

}
